using System;
using System.Collections.Generic;
using Glue.Internals.Nulls;

namespace Glue.Internals
{
    internal class MappingMapping<T1,T2> : IHoldNestedRelation
    {
        private readonly Mapping<T1, T2> map;

        public MappingMapping(Mapping<T1, T2> map)
        {
            this.map = map;
        }

        public object Map(object from, object to)
        {
            if (from == null)
                return null;
            if (from.GetType() == LeftType  ||  LeftType.IsInstanceOfType(from))
            {
                if (to==null)
                    ThrowToObjectIsNull(typeof(T1).Name, typeof(T2).Name);
                to = map.Map((T1)from, (T2)to);
            }
            else
            {
                if (to == null)
                    ThrowToObjectIsNull(typeof(T2).Name, typeof(T1).Name);
                to = map.Map((T2)from, (T1)to);
            }
                
            return to;
        }

        public List<IRelation> GetRelationsTowardsRight()
        {
            return map.Relations.GetRelationsTowardsRight();
        }

        public List<IRelation> GetRelationsTowardsLeft()
        {
            return map.Relations.GetRelationsTowardsLeft();
        }

        public IEnumerable<IRelation> GetAllRelations()
        {
            return map.Relations.GetAllRelations();
        }

        public bool IsRelatedTowardsOnLeftSide(IObjectMember member)
        {
            return map.Relations.IsRelatedTowardsOnLeftSide(member);
        }

        public bool IsRelatedTowardsOnRightSide(IObjectMember member)
        {
            return map.Relations.IsRelatedTowardsOnRightSide(member);
        }

        public Maybe<Func<object, object>> GetCreatorTowardsLeft()
        {
            return !map.LeftTypeCreator.HasValue() ? 
                Maybe<Func<object, object>>.Nothing() : 
                new Maybe<Func<object, object>>(x => map.LeftTypeCreator.GetValue()((T2)x));
        }

        public Maybe<Func<object, object>> GetCreatorTowardsRight()
        {
            return !map.RightTypeCreator.HasValue() ?
                Maybe<Func<object, object>>.Nothing() :
                new Maybe<Func<object, object>>(x => map.RightTypeCreator.GetValue()((T1)x));
        }

        private void ThrowToObjectIsNull(String fromTypeName, String toTypeName)
        {
            throw new GlueException("No object was set when trying to map from type '"+fromTypeName+"' to type '"+toTypeName+"'. Did you remember to instanciate the member of type '"+toTypeName+"'?");
        }

        public Type LeftType
        {
            get { return typeof(T1); }
        }

        public Type RightType
        {
            get { return typeof(T2); }
        }
    }
}